package models;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import answers.IAnswer;
import answers.Response;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

import views.SurveyView;
import controllers.ControllerMonitor;
import controllers.SurveyController;
import main.ConsoleInterpreterSingleton;
import mvc.AbstractModel;
import mvc.AbstractView;


public class SurveyModel extends AbstractModel
{
	public SurveyModel(String name, String caption)
	{
		super(name, caption);
		itsAbstractQuestions = new LinkedList<AbstractQuestion>();
		setEditMode(true);
		monitor = new ControllerMonitor();
	}
	
	public void addQuestion(AbstractQuestion question)
	{
		itsAbstractQuestions.add(question);
		setCaption("Added question at position " + size());
		notifyObservers();
	}
	
	public void deleteQuestion(int position)
	{
		if(position - 1 <= itsAbstractQuestions.size())
		{
			itsAbstractQuestions.remove(position -1);
			setCaption("Deleted question at position " + position);
		}
		else
		{
			setCaption("There are only " + size() + " questions in this survey, please try again!");
		}
		notifyObservers();
	}
	
	public void viewQuestion(int position)
	{
		if(position - 1 <= size())
		{
			AbstractQuestion question = itsAbstractQuestions.get(position - 1);
			
			if(question != null)
			{
				setCaption("Question #" + position + " [" + question.getCategory() + "]");
				notifyObservers();
				question.createView();
				question.notifyObservers();
			}
		}
		else
		{
			setCaption("There are only " + size() + " questions in this survey, please try again!");
		}
	}
	
	public void editQuestion(int position, AbstractQuestion editedQuestion)
	{
		if(position - 1 <= size())
		{
			editedQuestion.createView();
			itsAbstractQuestions.set(position - 1, editedQuestion);
			setCaption("Edited question at position " + position);
		}
		else
		{
			setCaption("There are only " + size() + " questions in this survey, please try again!");
		}
		notifyObservers();
	}
	
	public void viewSurvey()
	{
		int position = 1;
		Iterator<AbstractQuestion> iter = itsAbstractQuestions.iterator();
		while(iter.hasNext())
		{
			AbstractQuestion question = iter.next();
			setCaption("Question #" + position+ " [" + question.getCategory() + "]"); notifyObservers();
			question.createView();
			if(editMode == false)
			{
				question.setPermission(1);
			}
			question.notifyObservers();
			setCaption("\n"); notifyObservers();
			++position;
		}
		setCaption("end survey");
		notifyObservers();
	}
	
	public int size()
	{
		return itsAbstractQuestions.size();
	}
	
	public void take(SurveyResultsModel results)
	{
		currentResults = results;
		setCaption("\n This is your survey:\n");
		notifyObservers();
		setEditMode(false);
		viewSurvey();
		setCaption("\n type ? for instructions on how to answer the survey");
		notifyObservers();
	}
	
	public void respond(Response response)
	{
		selfGrade(response);
		// check to see if there already is a response for this question
		currentResults.removeResponse(response.getQuestionPosition());
		currentResults.addResponse(response);
		if(currentResults.size() == itsAbstractQuestions.size())
		{
			// survey is done
			finishTake();
		}
	}
	
	private void selfGrade(Response response)
	{
		int questionPosition = response.getQuestionPosition();
		AbstractQuestion question = itsAbstractQuestions.get(questionPosition - 1);
		IAnswer savedAnswer = question.getItsIAnswer();
		if(savedAnswer != null)
		{
			numGraded++;
			if(savedAnswer.compare(response.getItsIAnswer())) 
			{
				response.setScore(question.getPoints());
			}
		}
	}
	
	private void finishTake()
	{
		if(numGraded == size())
		{
			currentResults.setStatus("graded");
		}
		numGraded = 0;
		setEditMode(true);
		SurveyResultsModel.save(currentResults);
		currentResults = null;
		setCaption("All questions have been answered! back to desktop view");
		notifyObservers();
		ConsoleInterpreterSingleton interpreter = ConsoleInterpreterSingleton.getInstance();
		interpreter.pipeEventsTo(getControllerMonitorView());
	}
	
	public static void save(AbstractModel survey)
	{
		survey.cleanup();
		
		FileOutputStream out;
		try
		{
			out = new FileOutputStream(survey.getName());
			xstream.toXML(survey, out);
			out.close();
		} 
		catch (FileNotFoundException e)
		{
			System.out.println("File " + survey.getName() + " not found \\ not legal");
			e.printStackTrace();
		} 
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	
	@Override
	public AbstractView createView()
	{
		SurveyController controller = new SurveyController(this);
		AbstractView view  = new SurveyView(controller, this);
		this.attach(view);
		return view;
	}
	
	public int getLastResponsePosition()
	{
		int retval = -1;
		if(currentResults != null)
		{
			retval = currentResults.size();
		}
		
		return retval;
	}
	
	public void cleanup()
	{
		Iterator<AbstractQuestion> iter = itsAbstractQuestions.iterator();
		while(iter.hasNext())
		{
			AbstractQuestion question = iter.next();
			if(question != null)
			{
				question.cleanup();
			}
		}
		super.cleanup();
	}
	
	public void setControllerMonitorView(AbstractView view)
	{
		monitor.setView(view);
	}
	
	public AbstractView getControllerMonitorView()
	{
		return monitor.getView();
	}
	
	public void setEditMode(boolean editMode)
	{
		this.editMode = editMode;
	}

	public boolean getEditMode()
	{
		return editMode;
	}
	
	public AbstractQuestion getAbstractQuestion(int position)
	{
		if(position <= itsAbstractQuestions.size())
		{
			return itsAbstractQuestions.get(position -1);
		}
		else
		{
			return null;
		}
	}

	
	private List<AbstractQuestion> itsAbstractQuestions;
	private static XStream xstream = new XStream(new DomDriver());
	private boolean editMode;
	private int numGraded = 0;
	private ControllerMonitor  monitor;
	private SurveyResultsModel currentResults;
}
